1 /**
2 This is basically std.typecons.Nullable with extra features.
3 This module contains:
4   $(TOC isNullable)
5   $(TOC Nullable)
6 
7 Copyright: Copyright the respective authors, 2008-
8 License:   $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
9 Authors:   $(WEB erdani.org, Andrei Alexandrescu),
10            $(WEB bartoszmilewski.wordpress.com, Bartosz Milewski),
11            Don Clugston,
12            Shin Fujishiro,
13            Kenji Hara,
14            Matthew Armbruster
15 
16 $(B Source:) $(SRC $(SRCFILENAME))
17  */
18 module db_constraints.utils.nullable;
19 
20 import std.traits : isBuiltinType;
21 
22 /**
23 Checks if you can assign the value to a $(D Nullable!T)
24  */
25 template isNullable(T, I)
26 {
27     enum isNullable = __traits(compiles,
28                                (I i)
29                                {
30                                    Nullable!T test = i;
31                                });
32 }
33 
34 /**
35 Defines a value paired with a distinctive "null" state that denotes
36 the absence of a value. If default constructed, a $(D
37 Nullable!T) object starts in the null state. Assigning it renders it
38 non-null. Calling $(D nullify) can nullify it again.
39 Practically $(D Nullable!T) stores a $(D T) and a $(D bool).
40 
41 See_Also: $(LINK http://dlang.org/phobos/std_typecons.html#.Nullable)
42 
43  */
44 struct Nullable(T)
45 {
46     private T _value;
47     private bool _isNull = true;
48 
49 /**
50 Constructor initializing $(D this) with $(D value).
51 Params:
52     value = The value to initialize this $(D Nullable) with
53  */
54     this(inout T value) inout
55     {
56         _value = value;
57         _isNull = false;
58     }
59     this(N : typeof(null))(N n) inout nothrow pure @safe @nogc
60     {
61     }
62 
63     template toString()
64     {
65         import std.format : FormatSpec, formatValue;
66         // Needs to be a template because of DMD @@BUG@@ 13737.
67         void toString()(scope void delegate(const(char)[]) sink, FormatSpec!char fmt)
68         {
69             if (isNull)
70             {
71                 sink.formatValue("Nullable.null", fmt);
72             }
73             else
74             {
75                 sink.formatValue(_value, fmt);
76             }
77         }
78 
79         // Issue 14940
80         void toString()(scope void delegate(const(char)[]) @safe sink, FormatSpec!char fmt)
81         {
82             if (isNull)
83             {
84                 sink.formatValue("Nullable.null", fmt);
85             }
86             else
87             {
88                 sink.formatValue(_value, fmt);
89             }
90         }
91     }
92 
93 /**
94 Check if $(D this) is in the null state.
95 Returns:
96     true $(B iff) $(D this) is in the null state, otherwise false.
97 
98  */
99     @property bool isNull() const @safe pure nothrow @nogc
100     {
101         return _isNull;
102     }
103 
104 ///
105 unittest
106 {
107     Nullable!int ni;
108     assert(ni.isNull);
109 
110     ni = 0;
111     assert(!ni.isNull);
112 }
113 
114 // Issue 14940
115 @safe unittest
116 {
117     import std.array : appender;
118     import std.format : formattedWrite;
119 
120     auto app = appender!string();
121     Nullable!int a = 1;
122     formattedWrite(app, "%s", a);
123     assert(app.data == "1");
124 }
125 
126 
127 
128 
129 
130 
131     static if (!is(T == immutable(T)) && !is(T == const(T)))
132     {
133 /**
134 Assigns $(D value) to the internally-held state. If the assignment
135 succeeds, $(D this) becomes non-null.
136 Params:
137     value = A value of type $(D T) to assign to this $(D Nullable)
138  */
139         void opAssign()(T value)
140         {
141             _value = value;
142             _isNull = false;
143         }
144         /// ditto
145         void opAssign(N : typeof(null))(N n) @safe nothrow pure @nogc
146         {
147             this.nullify();
148         }
149 /**
150 Forces $(D this) to the null state.
151  */
152         void nullify()() @safe nothrow pure @nogc
153         {
154             .destroy(_value);
155             _isNull = true;
156         }
157     }
158 
159 ///
160 unittest
161 {
162     Nullable!int ni = 0;
163     assert(!ni.isNull);
164 
165     ni.nullify();
166     assert(ni.isNull);
167 }
168 
169 
170 unittest
171 {
172     //Passes
173     Nullable!(int*) npi;
174     assert(npi.isNull);
175 
176     npi = null;
177     assert(npi.isNull);
178 }
179     static if (__traits(compiles, (T a, T b) { return a == b; }) && !is(T == class))
180     {
181         bool opEquals(N : typeof(null))(N n) const nothrow pure @safe @nogc
182         {
183             return this.isNull;
184         }
185         bool opEquals(inout T rhs) const nothrow pure
186         {
187             bool result = false;
188             if (!this.isNull)
189             {
190                 result = (this._value == rhs);
191             }
192             return result;
193         }
194         bool opEquals(Nullable!T rhs) const
195         {
196             bool result = false;
197             if (!rhs.isNull)
198             {
199                 result = this.opEquals(rhs.get);
200             }
201             else if (!this.isNull)
202             {
203                 result = false;
204             }
205             else
206             {
207                 result = true;
208             }
209             return result;
210         }
211     }
212     static if (__traits(compiles, (T a, T b) { return a > b; }) && !is(T == class))
213     {
214         int opCmp(N : typeof(null))(N n) const nothrow pure @safe @nogc
215         {
216             return (this.isNull ? 0 : 1);
217         }
218         int opCmp(inout T rhs) const nothrow pure
219         {
220             int result = -1;
221             if (!this.isNull)
222             {
223                 if (this._value < rhs)
224                 {
225                     result = -1;
226                 }
227                 else if (this._value > rhs)
228                 {
229                     result = 1;
230                 }
231                 else
232                 {
233                     result = 0;
234                 }
235             }
236             return result;
237         }
238         int opCmp(Nullable!T rhs) const nothrow pure
239         {
240             int result = 0;
241             if (!rhs.isNull)
242             {
243                 result = this.opCmp(rhs.get);
244             }
245             else if (!this.isNull)
246             {
247                 result = 1;
248             }
249             return result;
250         }
251     }
252 
253 
254 /**
255 Gets the value. $(D this) must not be in the null state.
256 This function is also called for the implicit conversion to $(D T).
257 Returns:
258     The value held internally by this $(D Nullable).
259 
260 $(B Precondition:) $(D_CODE assert(!isNull);)
261  */
262     @property ref inout(T) get() inout @safe pure nothrow
263     in
264     {
265         enum message = "Called `get' on null Nullable!" ~ T.stringof ~ ".";
266         assert(!isNull, message);
267     }
268     body
269     {
270         return _value;
271     }
272 /**
273 Gets the value or the default value passed in.
274 Returns:
275     The value held internally by this $(D Nullable) or the extra value passed in.
276  */
277     auto ref inout(T) getValueOr(lazy inout T defVal) inout
278     {
279         return (this.isNull ? defVal : this.get);
280     }
281 
282 ///
283 unittest
284 {
285     import std.exception: assertThrown, assertNotThrown;
286 
287     Nullable!int ni;
288     //`get` is implicitly called. Will throw
289     //an AssertError in non-release mode
290     assertThrown!Throwable(ni += 1);
291 
292     ni = 0;
293     assertNotThrown!Throwable(ni += 1);
294 }
295 
296 /**
297 Implicitly converts to $(D T).
298 $(D this) must not be in the null state.
299  */
300     alias get this;
301 }
302 
303 ///
304 unittest
305 {
306     struct CustomerRecord
307     {
308         string name;
309         string address;
310         int customerNum;
311     }
312 
313     Nullable!CustomerRecord getByName(string name)
314     {
315         //A bunch of hairy stuff
316 
317         return Nullable!CustomerRecord.init;
318     }
319 
320     auto queryResult = getByName("Doe, John");
321     if (!queryResult.isNull)
322     {
323         //Process Mr. Doe's customer record
324         auto address = queryResult.address;
325         auto customerNum = queryResult.customerNum;
326 
327         //Do some things with this customer's info
328     }
329     else
330     {
331         //Add the customer to the database
332     }
333 }
334 
335 unittest
336 {
337     Nullable!int i;
338     assert(i.isNull);
339     i = 3;
340     assert(!i.isNull);
341     i = null;
342     assert(i.isNull);
343 }
344 
345 unittest
346 {
347     Nullable!int i = 3;
348     assert(i.get == 3);
349     assert(i.getValueOr(4) == 3);
350     i = null;
351     assert(i.isNull && i.getValueOr(4) == 4);
352 }
353 
354 unittest
355 {
356     Nullable!int i = null;
357     assert(i == null);
358     i = 3;
359     assert(i == 3);
360     Nullable!int j;
361     assert(i != j);
362     assert(i == j.getValueOr(3));
363     int returns3(ref int count)
364     {
365         count += 1;
366         return 3;
367     }
368     int lazycalls = 0;
369     assert(i == j.getValueOr(returns3(lazycalls)));
370     assert(lazycalls == 1);
371     i = 2;
372     j = 2;
373     assert(i == j.getValueOr(returns3(lazycalls)));
374     assert(lazycalls == 1);
375 }
376 
377 unittest
378 {
379     Nullable!int i;
380     Nullable!int j;
381     assert(i == j);
382     i = 5;
383     j = 6;
384     assert(i != j);
385 }
386 
387 unittest
388 {
389     Nullable!int i;
390     Nullable!int j = 3;
391     assert(i < j);
392     i = 5;
393     j = 6;
394     assert(i != j);
395 }
396 
397 unittest
398 {
399     import std.string;
400     struct Example
401     {
402         string name;
403         int number;
404         bool opEquals(inout(Example) ex) const pure nothrow @nogc @safe
405         {
406             return (this.name == ex.name && this.number == ex.number);
407         }
408     }
409     Nullable!Example i;
410     assert(i.isNull);
411     assert(i == null);
412     i = Example("Tom", 9);
413     assert(!i.isNull);
414     assert(i != null);
415     auto j = Example("Tom", 9);
416     assert(i == j);
417     i = null;
418     assert(i != j);
419 }
420 
421 unittest
422 {
423     import std.exception : assertThrown;
424 
425     Nullable!int a;
426     assert(a.isNull);
427     assertThrown!Throwable(a.get);
428     a = 5;
429     assert(!a.isNull);
430     assert(a == 5);
431     assert(a != 3);
432     assert(a.get != 3);
433     a.nullify();
434     assert(a.isNull);
435     a = 3;
436     assert(a == 3);
437     a *= 6;
438     assert(a == 18);
439     a = a;
440     assert(a == 18);
441     a.nullify();
442     assertThrown!Throwable(a += 2);
443 }
444 unittest
445 {
446     auto k = Nullable!int(74);
447     assert(k == 74);
448     k.nullify();
449     assert(k.isNull);
450 }
451 unittest
452 {
453     static int f(in Nullable!int x) {
454         return x.isNull ? 42 : x.get;
455     }
456     Nullable!int a;
457     assert(f(a) == 42);
458     a = 8;
459     assert(f(a) == 8);
460     a.nullify();
461     assert(f(a) == 42);
462 }
463 unittest
464 {
465     import std.exception : assertThrown;
466 
467     static struct S { int x; }
468     Nullable!S s;
469     assert(s.isNull);
470     s = S(6);
471     assert(s == S(6));
472     assert(s != S(0));
473     assert(s.get != S(0));
474     s.x = 9190;
475     assert(s.x == 9190);
476     s.nullify();
477     assertThrown!Throwable(s.x = 9441);
478 }
479 unittest
480 {
481     // Ensure Nullable can be used in pure/nothrow/@safe environment.
482     function() @safe pure nothrow
483     {
484         Nullable!int n;
485         assert(n.isNull);
486         n = 4;
487         assert(!n.isNull);
488         assert(n == 4);
489         n.nullify();
490         assert(n.isNull);
491     }();
492 }
493 unittest
494 {
495     // Ensure Nullable can be used when the value is not pure/nothrow/@safe
496     static struct S
497     {
498         int x;
499         this(this) @system {}
500     }
501 
502     Nullable!S s;
503     assert(s.isNull);
504     s = S(5);
505     assert(!s.isNull);
506     assert(s.x == 5);
507     s.nullify();
508     assert(s.isNull);
509 }
510 unittest
511 {
512     // Bugzilla 9404
513     alias N = Nullable!int;
514 
515     void foo(N a)
516     {
517         N b;
518         b = a; // `N b = a;` works fine
519     }
520     N n;
521     foo(n);
522 }
523 unittest
524 {
525     //Check nullable immutable is constructable
526     {
527         auto a1 = Nullable!(immutable int)();
528         auto a2 = Nullable!(immutable int)(1);
529         auto i = a2.get;
530     }
531     //Check immutable nullable is constructable
532     {
533         auto a1 = immutable (Nullable!int)();
534         auto a2 = immutable (Nullable!int)(1);
535         auto i = a2.get;
536     }
537 }
538 unittest
539 {
540     alias NInt   = Nullable!int;
541 
542     //Construct tests
543     {
544         //from other Nullable null
545         NInt a1;
546         NInt b1 = a1;
547         assert(b1.isNull);
548 
549         //from other Nullable non-null
550         NInt a2 = NInt(1);
551         NInt b2 = a2;
552         assert(b2 == 1);
553 
554         //Construct from similar nullable
555         auto a3 = immutable(NInt)();
556         NInt b3 = a3;
557         assert(b3.isNull);
558     }
559 
560     //Assign tests
561     {
562         //from other Nullable null
563         NInt a1;
564         NInt b1;
565         b1 = a1;
566         assert(b1.isNull);
567 
568         //from other Nullable non-null
569         NInt a2 = NInt(1);
570         NInt b2;
571         b2 = a2;
572         assert(b2 == 1);
573 
574         //Construct from similar nullable
575         auto a3 = immutable(NInt)();
576         NInt b3 = a3;
577         b3 = a3;
578         assert(b3.isNull);
579     }
580 }
581 unittest
582 {
583     import std.meta : AliasSeq;
584     //Check nullable is nicelly embedable in a struct
585     static struct S1
586     {
587         Nullable!int ni;
588     }
589     static struct S2 //inspired from 9404
590     {
591         Nullable!int ni;
592         this(S2 other)
593         {
594             ni = other.ni;
595         }
596         void opAssign(S2 other)
597         {
598             ni = other.ni;
599         }
600     }
601     foreach (S; AliasSeq!(S1, S2))
602     {
603         S a;
604         S b = a;
605         S c;
606         c = a;
607     }
608 }
609 unittest
610 {
611     // Bugzilla 10268
612     import std.json;
613     JSONValue value = null;
614     auto na = Nullable!JSONValue(value);
615 
616     struct S1 { int val; }
617     struct S2 { int* val; }
618     struct S3 { immutable int* val; }
619 
620     {
621         auto sm = S1(1);
622         immutable si = immutable S1(1);
623         static assert( __traits(compiles, { auto x1 =           Nullable!S1(sm); }));
624         static assert( __traits(compiles, { auto x2 = immutable Nullable!S1(sm); }));
625         static assert( __traits(compiles, { auto x3 =           Nullable!S1(si); }));
626         static assert( __traits(compiles, { auto x4 = immutable Nullable!S1(si); }));
627     }
628 
629     auto nm = 10;
630     immutable ni = 10;
631 
632     {
633         auto sm = S2(&nm);
634         immutable si = immutable S2(&ni);
635         static assert( __traits(compiles, { auto x =           Nullable!S2(sm); }));
636         static assert(!__traits(compiles, { auto x = immutable Nullable!S2(sm); }));
637         static assert(!__traits(compiles, { auto x =           Nullable!S2(si); }));
638         static assert( __traits(compiles, { auto x = immutable Nullable!S2(si); }));
639     }
640 
641     {
642         auto sm = S3(&ni);
643         immutable si = immutable S3(&ni);
644         static assert( __traits(compiles, { auto x =           Nullable!S3(sm); }));
645         static assert( __traits(compiles, { auto x = immutable Nullable!S3(sm); }));
646         static assert( __traits(compiles, { auto x =           Nullable!S3(si); }));
647         static assert( __traits(compiles, { auto x = immutable Nullable!S3(si); }));
648     }
649 }
650 unittest
651 {
652     // Bugzila 10357
653     import std.datetime;
654     Nullable!SysTime time = SysTime(0);
655 }
656 unittest
657 {
658     import std.conv: to;
659     import std.array;
660 
661     // Bugzilla 10915
662     Appender!string buffer;
663 
664     Nullable!int ni;
665     assert(ni.to!string() == "Nullable.null");
666 
667     struct Test { string s; }
668     alias NullableTest = Nullable!Test;
669 
670     NullableTest nt = Test("test");
671     assert(nt.to!string() == `Test("test")`);
672 
673     NullableTest ntn = Test("null");
674     assert(ntn.to!string() == `Test("null")`);
675 
676     class TestToString
677     {
678         double d;
679 
680         this (double d)
681         {
682             this.d = d;
683         }
684 
685         override string toString()
686         {
687             return d.to!string();
688         }
689     }
690     Nullable!TestToString ntts = new TestToString(2.5);
691     assert(ntts.to!string() == "2.5");
692 }